package com.zkh.myutils.database.data;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.zkh.myutils.annotation.Column;
import com.zkh.myutils.database.DatabaseException;
import com.zkh.myutils.database.data.tools.BeanMethodInvokeBean;
import com.zkh.myutils.database.data.tools.TypeAdapter;
import com.zkh.myutils.io.IOUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.utils.StringUtils;

/**
 * 读取数据工具
 * @author zkh
 *
 */
public class DBOperateUtils {
	
	/**
	 * 读取结果集，并解析成Map集合。<br />
	 * <b>注：Oracle数据库number类型将统一被转为java.math.BigDecimal类型</b>
	 * @param rs ResultSet对象
	 * @param toLowerCase 数据库字段名是否转为小写
	 */
	public static List<Map<String, Object>> getMapList(ResultSet rs, boolean toLowerCase) throws SQLException{
		return mapListRender(rs, null, null, toLowerCase);
	}
	
	/**
	 * 处理MapList值
	 * @param rs ResultSet对象
	 * @param entityList 实体类集合，用来存放Map转Bean的结果集
	 * @param entityClass 实体类Class
	 * @param toLowerCase 数据库字段名是否转为小写
	 * @return 当entityList和entityClass同时不为null时，返回空集合，map转bean的结果直接放入entityList集合中
	 */
	public static <T> List<Map<String, Object>> mapListRender(ResultSet rs, List<T> entityList, Class<? extends T> entityClass, boolean toLowerCase) throws SQLException{
		//数据容器
		List<Map<String, Object>> ctn = new ArrayList<Map<String, Object>>();
		//元数据
		ResultSetMetaData meta = rs.getMetaData();
		//字段数
		int c = meta.getColumnCount();
		//字段名
		List<String> cons = new ArrayList<>(), cnns = new ArrayList<>();
		//是否转换Bean
		boolean transBean = entityClass!=null;
		//Map转Bean中间变量
		int[] typeArray = new int[c];	//类型容器
		List<BeanMethodInvokeBean> methodCacheList = new ArrayList<>();	//Map转Bean方法缓存
		//遍历字段名
		for(int i=0;i<c;i++){
			//名称
			String name = meta.getColumnLabel(i+1), newName = toLowerCase ? name.toLowerCase() : name;
			//保存
			cons.add(name); cnns.add(newName);
			// 装载类型
			typeArray[i] = meta.getColumnType(i+1);
		}
		//Map转Bean
		if(transBean) {
			//读取数据
			while(rs.next()){
				//创建实体
				T en = null;
				try {
					en = entityClass.newInstance();
				} catch (IllegalAccessException e) {
					throw new DatabaseException("请提供可访问的无参构造器");
				} catch (InstantiationException e) {
					throw new DatabaseException("实体类实例化失败");
				}
				//保存实体对象
				entityList.add(en);
				//缓存方法
				if(methodCacheList.size()==0) {
					//获取字段
					Field[] fields = entityClass.getDeclaredFields();
					//遍历字段名
					for(int i=0; i<c; i++) {
						//数据库字段名
						String fieldName = cons.get(i);
						//获取bean属性索引
						int index = findField(fieldName, fields);
						//存在setter
						if(index>-1) {
							//字段对象
							Field f = fields[index];
							//获取bean中对应的setter
							String setter = "set" + StringUtils.uppercaseFirst(f.getName());
							try {
								//获取setter并调用
								Method method = entityClass.getMethod(setter, f.getType());
								//保存缓存
								methodCacheList.add(new BeanMethodInvokeBean(f.getName(), f.getType(), setter, method));
							}catch (NoSuchMethodException e) {
								//setter不存在
								methodCacheList.add(null);
							}
						}else {//实体类中不存在对应字段
							methodCacheList.add(null);
						}
					}
				}
				//遍历字段映射
				for(int i=0; i<c; i++) {
					//获取方法
					BeanMethodInvokeBean b = methodCacheList.get(i);
					//有setter，则进行处理
					if(b!=null) {
						//获取值并修正类型
						Object val = TypeAdapter.typeFix(rs.getObject(cons.get(i)), typeArray[i], b.getFieldType());
						try {
							//获取setter并调用
							b.getMethod().invoke(en, val);
						}catch(Exception e) {
							Logger.error(e);
						}
					}
				}
			}
		}else {
			//读取数据
			while(rs.next()){
				//Map映射
				Map<String, Object> map = new HashMap<String, Object>();
				//遍历
				for(int i=0; i<c; i++) {
					map.put(cnns.get(i), TypeAdapter.typeFixForMap(rs.getObject(cons.get(i)), typeArray[i]));
				}
				//保存
				ctn.add(map);
			}
		}
		return ctn;
	}
	
	/**
	 * 读取结果集，并解析成数组集合
	 * @param rs ResultSet对象
	 */
	public static List<Object[]> getArrayList(ResultSet rs) throws SQLException{
		return getArrayList(rs, null);
	}
	
	/**
	 * 读取结果集，并解析成数组集合
	 * @param rs ResultSet对象
	 * @param typeList 字段类型列表，不为null，就将类型依次写入集合
	 */
	public static List<Object[]> getArrayList(ResultSet rs, List<Integer> typeList) throws SQLException{
		//容器
		List<Object[]> ctn = new ArrayList<Object[]>();
		//元数据
		ResultSetMetaData meta = rs.getMetaData();
		//字段数
		int c = meta.getColumnCount();
		//读取数据
		while(rs.next()){
			//数组
			Object[] obj = new Object[c];
			//遍历
			for(int i=0;i<c;i++){
				obj[i] = rs.getObject(i+1);
			}
			//保存
			ctn.add(obj);
		}
		//读取类型，如果有
		if(typeList!=null) {
			//遍历列类型
			for(int i=1; i<=c; i++) {
				typeList.add(meta.getColumnType(i));
			}
		}
		return ctn;
	}
	
	/**
	 * 解析单行单列的数据
	 * @param rs ResultSet对象
	 */
	public static Object getResult(ResultSet rs) throws SQLException{
		//查看是否有数据
		if(!rs.next()){
			return null;
		}
		return rs.getObject(1);
	}
	
	/**
	 * 解析单列多行的数据(多列数据只取第一列)
	 * @param rs ResultSet对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<T> getColumnList(ResultSet rs) throws SQLException{
		return getColumnList(rs, v->(T) v);
	}
	
	/**
	 * 解析单列多行的数据(多列数据只取第一列)
	 * @param rs ResultSet对象
	 * @param typeConverter 类型转换器，当前默认类型和所需类型不一致时，传入自定义类型转换逻辑
	 */
	public static <T> List<T> getColumnList(ResultSet rs, Function<Object, T> typeConverter) throws SQLException{
		//容器
		List<T> ctn = new ArrayList<T>();
		//读取数据
		while(rs.next()){
			ctn.add(typeConverter.apply(rs.getObject(1)));
		}
		//返回
		return ctn;
	}
	
	/**
	 * 关闭ResultSet对象
	 */
	public static void close(ResultSet rs){
		//静默关闭
		IOUtils.closeQuietly(rs, e->new DatabaseException("关闭ResultSet对象时出错", e));
	}
	
	/**
	 * 关闭PreparedStatement对象
	 */
	public static void close(PreparedStatement pstmt){
		//静默关闭
		IOUtils.closeQuietly(pstmt, e->new DatabaseException("关闭PreparedStatement对象时出错", e));
	}
	
	/**
	 * 关闭Statement对象
	 */
	public static void close(Statement stmt){
		//静默关闭
		IOUtils.closeQuietly(stmt, e->new DatabaseException("关闭Statement对象时出错", e));
	}
	
	/**
	 * 关闭Connection对象
	 * @param conn 连接对象
	 */
	public static void close(Connection conn){
		//静默关闭
		IOUtils.closeQuietly(conn, e->new DatabaseException("关闭Connection对象时出错", e));
	}
	
	/**
	 * 匹配字段，并获取字段索引位置
	 * @param fieldName 数据库字段
	 * @param fields bean字段field
	 * @return
	 */
	private static int findField(String fieldName, Field[] fields) {
		//遍历
		for(int i=0, len=fields.length; i<len; i++) {
			//获取字段或列注解
			Field field = fields[i]; Column column;
			//匹配相等
			if(fieldName.equalsIgnoreCase(field.getName()) ||
					((column=field.getAnnotation(Column.class))!=null && fieldName.equalsIgnoreCase(column.name()))) {
				return i;
			}
		}
		//未匹配到
		return -1;
	}
}
